<!doctype html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>ESP32CAM远程监控3D打印机</title>
    <style>
        body {
            font-family: Arial, Helvetica, sans-serif;
            background: #181818;
            color: #EFEFEF;
            font-size: 16px
        }

        h2 {
            font-size: 18px
        }

        section.main {
            display: flex
        }

        #menu,
        section.main {
            flex-direction: column
        }

        #menu {
            flex-wrap: nowrap;
            min-width: 340px;
            background: #363636;
            padding: 8px;
            border-radius: 4px;
            margin-top: -10px;
            margin-right: 10px;
        }

        #content {
            display: flex;
            flex-wrap: wrap;
            align-items: stretch
        }

        figure {
            padding: 0px;
            margin: 0;
            -webkit-margin-before: 0;
            margin-block-start: 0;
            -webkit-margin-after: 0;
            margin-block-end: 0;
            -webkit-margin-start: 0;
            margin-inline-start: 0;
            -webkit-margin-end: 0;
            margin-inline-end: 0
        }

        figure img {
            display: block;
            width: 100%;
            height: auto;
            border-radius: 4px;
            margin-top: 8px;
        }

        /*By HaPiWan*/
        #logo_img {
            width: 100%;
            height: 93px;
            min-width: 300px;
            text-align: center;
            margin-bottom: 20px;
        }

        #logo_img img {
            width: 300px;
            height: 93px;
        }

        @media (min-width: 800px) and (orientation:landscape) {
            #content {
                display: flex;
                flex-wrap: nowrap;
                align-items: stretch
            }

            figure img {
                display: block;
                max-width: 100%;
                max-height: calc(100vh - 40px);
                width: auto;
                height: auto
            }

            figure {
                padding: 0 0 0 0px;
                margin: 0;
                -webkit-margin-before: 0;
                margin-block-start: 0;
                -webkit-margin-after: 0;
                margin-block-end: 0;
                -webkit-margin-start: 0;
                margin-inline-start: 0;
                -webkit-margin-end: 0;
                margin-inline-end: 0
            }
        }

        section#buttons {
            display: flex;
            flex-wrap: nowrap;
            justify-content: space-between
        }

        #nav-toggle {
            cursor: pointer;
            display: block
        }

        #nav-toggle-cb {
            outline: 0;
            opacity: 0;
            width: 0;
            height: 0
        }

        #nav-toggle-cb:checked+#menu {
            display: flex
        }

        .input-group {
            display: flex;
            flex-wrap: nowrap;
            line-height: 22px;
            margin: 5px 0
        }

        .input-group>label {
            display: inline-block;
            padding-right: 10px;
            min-width: 47%
        }

        .input-group input,
        .input-group select {
            flex-grow: 1
        }

        .range-max,
        .range-min {
            display: inline-block;
            padding: 0 5px
        }


        button,
        .button {
            display: block;
            margin: 5px;
            padding: 0 12px;
            border: 0;
            line-height: 28px;
            cursor: pointer;
            color: #fff;
            background: #2d8f06;
            border-radius: 5px;
            font-size: 16px;
            outline: 0
        }

        .save {
            position: absolute;
            right: 25px;
            top: 0px;
            height: 16px;
            line-height: 16px;
            padding: 0 4px;
            text-decoration: none;
            cursor: pointer
        }

        button:hover {
            background: rgb(100, 228, 128);
            color: #363636;
        }

        button:active {
            background: #206604
        }

        button.disabled {
            cursor: default;
            background: #a0a0a0
        }

        input[type=range] {
            -webkit-appearance: none;
            width: 100%;
            height: 22px;
            background: #363636;
            cursor: pointer;
            margin: 0
        }

        input[type=range]:focus {
            outline: 0
        }

        input[type=range]::-webkit-slider-runnable-track {
            width: 100%;
            height: 2px;
            cursor: pointer;
            background: #EFEFEF;
            border-radius: 0;
            border: 0 solid #EFEFEF
        }

        input[type=range]::-webkit-slider-thumb {
            border: 1px solid rgba(0, 0, 30, 0);
            height: 22px;
            width: 22px;
            border-radius: 50px;
            background: #ff3034;
            cursor: pointer;
            -webkit-appearance: none;
            margin-top: -11.5px
        }

        input[type=range]:focus::-webkit-slider-runnable-track {
            background: #EFEFEF
        }

        input[type=range]::-moz-range-track {
            width: 100%;
            height: 2px;
            cursor: pointer;
            background: #EFEFEF;
            border-radius: 0;
            border: 0 solid #EFEFEF
        }

        input[type=range]::-moz-range-thumb {
            border: 1px solid rgba(0, 0, 30, 0);
            height: 22px;
            width: 22px;
            border-radius: 50px;
            background: #ff3034;
            cursor: pointer
        }

        input[type=range]::-ms-track {
            width: 100%;
            height: 2px;
            cursor: pointer;
            background: 0 0;
            border-color: transparent;
            color: transparent
        }

        input[type=range]::-ms-fill-lower {
            background: #EFEFEF;
            border: 0 solid #EFEFEF;
            border-radius: 0
        }

        input[type=range]::-ms-fill-upper {
            background: #EFEFEF;
            border: 0 solid #EFEFEF;
            border-radius: 0
        }

        input[type=range]::-ms-thumb {
            border: 1px solid rgba(0, 0, 30, 0);
            height: 22px;
            width: 22px;
            border-radius: 50px;
            background: #ff3034;
            cursor: pointer;
            height: 2px
        }

        input[type=range]:focus::-ms-fill-lower {
            background: #EFEFEF
        }

        input[type=range]:focus::-ms-fill-upper {
            background: #363636
        }

        .switch {
            display: block;
            position: relative;
            line-height: 22px;
            font-size: 16px;
            height: 22px
        }

        .switch input {
            outline: 0;
            opacity: 0;
            width: 0;
            height: 0
        }

        .slider {
            width: 50px;
            height: 22px;
            border-radius: 22px;
            cursor: pointer;
            background-color: grey
        }

        .slider,
        .slider:before {
            display: inline-block;
            transition: .4s
        }

        .slider:before {
            position: relative;
            content: "";
            border-radius: 50%;
            height: 16px;
            width: 16px;
            left: 4px;
            top: 3px;
            background-color: #fff
        }

        input:checked+.slider {
            background-color: #ff3034
        }

        input:checked+.slider:before {
            -webkit-transform: translateX(26px);
            transform: translateX(26px)
        }

        select {
            border: 1px solid #363636;
            font-size: 14px;
            height: 22px;
            outline: 0;
            border-radius: 5px
        }

        .image-container {
            position: relative;
            min-width: 160px;
            min-height: 160px;
        }

        .close {
            position: absolute;
            right: 5px;
            top: 5px;
            background: #ff3034;
            width: 16px;
            height: 16px;
            border-radius: 100px;
            color: #fff;
            text-align: center;
            line-height: 18px;
            cursor: pointer
        }

        .hidden {
            display: none
        }
        /*By HaPiWan*/
        #reboot-container{height:30px; line-height: 30px; background-color: #363636; border-radius: 4px; color: #2d8f06; padding: 8px; margin-top: 20px; display: none;}
    </style>
</head>

<body>
    <section class="main">
        <!-- By HaPiWan -->
        <section id="logo">
            <div id="logo_img">
                <img
                    src="" />
            </div>
        </section>
        <div id="content">
            <div id="sidebar">
                <nav id="menu">
                    <div class="input-group" id="framesize-group">
                        <label for="framesize">分辨率：</label>
                        <select id="framesize" class="default-action">
                            <!-- 2MP -->
                            <option value="13">UXGA(1600x1200)</option>
                            <option value="12">SXGA(1280x1024)</option>
                            <option value="11">HD(1280x720)</option>
                            <option value="10">XGA(1024x768)</option>
                            <option value="9">SVGA(800x600)</option>
                            <option value="8">VGA(640x480)</option>
                            <option value="7">HVGA(480x320)</option>
                            <option value="6">CIF(400x296)</option>
                            <option value="5">QVGA(320x240)</option>
                            <option value="4">240x240</option>
                            <option value="3">HQVGA(240x176)</option>
                            <option value="2">QCIF(176x144)</option>
                            <option value="1">QQVGA(160x120)</option>
                            <option value="0">96x96</option>
                        </select>
                    </div>
                </nav>
            </div>

            <figure>
                <figure>
                    <div id="stream-container" class="image-container hidden">
                        <a id="save-still" href="#" class="button save" download="capture.jpg">保存</a>
                        <div class="close" id="close-stream">×</div>
                        <img id="stream" src="">
                    </div>
                </figure>
            </figure>
        </div>
        <!-- By HaPiWan 按钮区 -->
        <section id="buttons">
            <button id="toggle-led">开灯</button>
            <button id="get-still">抓拍</button>
            <button id="toggle-stream">观看视频</button>
            <button id="power-off-3d-printer">关机</button>
            <button id="reboot-esp32">重启ESP</button>
        </section>
        <div id="reboot-container">
            重启成功……10秒后页面自动刷新
        </div>
    </section>
    <script>
        document.addEventListener('DOMContentLoaded', function (event) {
            var baseHost = document.location.origin
            var streamUrl = baseHost + ':81'
            
            //By HaPiWan
            //FRPC配置的首页地址二级域名必须是esp32。提供图片流的服务地址二级域名必须是stream
            //例如 http://esp32.abc.com:8088 访问网页首页 ESP 80端口
            //例如 http://stream.abc.com:8088/stream 访问图片流
            if (baseHost.indexOf("esp32") > -1) {
                streamUrl = "http://" + window.location.host.replace("esp32","stream");
            }

            const hide = el => {
                el.classList.add('hidden')
            }
            const show = el => {
                el.classList.remove('hidden')
            }

            const disable = el => {
                el.classList.add('disabled')
                el.disabled = true
            }

            const enable = el => {
                el.classList.remove('disabled')
                el.disabled = false
            }

            const updateValue = (el, value, updateRemote) => {
                updateRemote = updateRemote == null ? true : updateRemote
                let initialValue
                if (el.type === 'checkbox') {
                    initialValue = el.checked
                    value = !!value
                    el.checked = value
                } else {
                    initialValue = el.value
                    el.value = value
                }

                if (updateRemote && initialValue !== value) {
                    updateConfig(el);
                } else if (!updateRemote) {
                    if (el.id === "aec") {
                        value ? hide(exposure) : show(exposure)
                    } else if (el.id === "agc") {
                        if (value) {
                            show(gainCeiling)
                            hide(agcGain)
                        } else {
                            hide(gainCeiling)
                            show(agcGain)
                        }
                    } else if (el.id === "awb_gain") {
                        value ? show(wb) : hide(wb)
                    } else if (el.id === "face_recognize") {
                        value ? enable(enrollButton) : disable(enrollButton)
                    }
                }
            }

            function updateConfig(el) {
                let value
                switch (el.type) {
                    case 'checkbox':
                        value = el.checked ? 1 : 0
                        break
                    case 'range':
                    case 'select-one':
                        value = el.value
                        break
                    case 'button':
                    case 'submit':
                        value = '1'
                        break
                    default:
                        return
                }

                const query = `${baseHost}/control?var=${el.id}&val=${value}`

                fetch(query)
                    .then(response => {
                        console.log(`request to ${query} finished, status: ${response.status}`)
                    })
            }

            document
                .querySelectorAll('.close')
                .forEach(el => {
                    el.onclick = () => {
                        hide(el.parentNode)
                    }
                })


            const view = document.getElementById('stream')
            const viewContainer = document.getElementById('stream-container')

            //By HaPiWan
            const stillButton = document.getElementById('get-still')
            const streamButton = document.getElementById('toggle-stream')
            const ledButton = document.getElementById('toggle-led')
            const closeButton = document.getElementById('close-stream')
            const rebootButton = document.getElementById('reboot-esp32')
            const poweroff = document.getElementById('power-off-3d-printer')

            const saveButton = document.getElementById('save-still')
            const framesize = document.getElementById('framesize')

            fetch(`${baseHost}/status`)
                .then(function (response) {
                    return response.json()
                })
                .then(function (state) {
                    console.log(`Get camera initial values finished.`)
                    console.log(state)
                    document
                        .querySelectorAll('.default-action')
                        .forEach(el => {
                            updateValue(el, state[el.id], false)
                            console.log(el.id + `:` + state[el.id])
                        })
                    if (state){
                        //By HaPiWan
                        //同步ESP32-CAM开发板当前 板载LED、继电器状态
                        ledButton.innerText = (state.led == 1) ? "关灯":"开灯"
                        poweroff.innerText = (state.power == 1) ? "开机":"关机"
                    }
                })

            //By HaPiWan 图像尺寸改变后，将配置同步到ESP32-CAM
            framesize.onchange = () => {
                updateConfig(framesize)
            }

            saveButton.onclick = () => {
                var canvas = document.createElement("canvas");
                canvas.width = view.width;
                canvas.height = view.height;
                document.body.appendChild(canvas);
                var context = canvas.getContext('2d');
                context.drawImage(view, 0, 0);
                try {
                    var dataURL = canvas.toDataURL('image/jpeg');
                    saveButton.href = dataURL;
                    var d = new Date();
                    saveButton.download = d.getFullYear() + ("0" + (d.getMonth() + 1)).slice(-2) + ("0" + d.getDate()).slice(-2) + ("0" + d.getHours()).slice(-2) + ("0" + d.getMinutes()).slice(-2) + ("0" + d.getSeconds()).slice(-2) + ".jpg";
                } catch (e) {
                    console.error(e);
                }
                canvas.parentNode.removeChild(canvas);
            }

            const stopStream = () => {
                window.stop();
                //By HaPiWan
                streamButton.innerHTML = '观看视频'
            }

            const startStream = () => {
                view.src = `${streamUrl}/stream`
                show(viewContainer)
                //By HaPiWan
                streamButton.innerHTML = '停止观看'
            }

            stillButton.onclick = () => {
                stopStream()
                view.src = `${baseHost}/capture?_cb=${Date.now()}`
                show(viewContainer)
            }

            closeButton.onclick = () => {
                stopStream()
                hide(viewContainer)
            }

            //By HaPiWan //重启ESP32-CAM
            rebootButton.onclick = () =>{
                const query = `${baseHost}/control?var=reboot&val=0`
                fetch(query)
                    .then(response => {
                        console.log(`Reboot ESP32-CAM finished, status:${response.status}`)
                        const reboot = document.getElementById('reboot-container')
                        if (response.status === 200){
                            var timeout = 10
                            var interval = setInterval(function(){
                                reboot.innerText = '重启成功……' + (--timeout) +'秒后页面自动刷新'
                                if (timeout <= 0){
                                    window.clearInterval(interval)
                                    window.location.reload()
                                }
                            }, 1000)
                        }else{
                            reboot.innerText = '重启失败'
                        }
                        reboot.style.display = 'block'
                    })
            }

            //By HaPiWan //控制继电器开关
            poweroff.onclick = () => {
                const powerEnabled = poweroff.innerHTML === '关机'
                const query = `${baseHost}/control?var=poweroff&val=${powerEnabled ? 1 : 0}`
                fetch(query)
                    .then(response => {
                        poweroff.innerHTML = (powerEnabled ? '开机' : '关机')
                        console.log(`request to ${query} finished, status: ${response.status}`)
                    })
            }
            //By HaPiWan //控制板载LED的开关，提供环境照明
            ledButton.onclick = () => {
                const ledEnabled = ledButton.innerHTML === '开灯'
                const query = `${baseHost}/control?var=led&val=${ledEnabled ? 1 : 0}`
                fetch(query)
                    .then(response => {
                        ledButton.innerHTML = (ledEnabled ? '关灯' : '开灯')
                        console.log(`request to ${query} finished, status: ${response.status}`)
                    })
            }

            streamButton.onclick = () => {
                //By HaPiWan //控制图片流的播放和停止
                const streamEnabled = streamButton.innerHTML === '停止观看'
                if (streamEnabled) {
                    stopStream()
                } else {
                    startStream()
                }
            }
        })

    </script>
</body>

</html>